<?php

namespace App\Cost;

class CostBuilder
{
    /**
     * @var CostInterface[]
     */
    private $costs = [];

    private CostType|null $costType;

    public function __construct(private $data = [
        'startMinutes' => 0,
        'startMinutesPrice' => 0,
        'exceedType' => '',
        'exceedFreeMinutes' => 0,
        'exceedStageMinutes' => 0,
        'exceedStagePriceMinutes' => 0,
        'maxPrice' => 0,

        'perHourPrice' => 0,
        'stageHours' => 0,
        'perStagePrice' => 0,
        'blockBookingStages' => [

        ],
        'exceedBlockBookingMinutes' => 0,
        'exceedPerHourPrice' => 0,
    ])
    {
        $this->bindCosts();
    }

    public function setData($data)
    {
        $this->data = $data;

        return $this;
    }

    public function build()
    {
        $cost = $this->getCostObj();
        $cost->startMinutes = $this->data['startMinutes'] ?? 0;
        $cost->startMinutesPrice = $this->data['startMinutesPrice'] ?? 0;
        $cost->exceedType = ExceedType::from($this->data['exceedType']);
        $cost->exceedFreeMinutes = $this->data['exceedFreeMinutes'] ?? 0;
        $cost->exceedStageMinutes = $this->data['exceedStageMinutes'] ?? 0;
        $cost->exceedStagePriceMinutes = $this->data['exceedStagePriceMinutes'] ?? 0;
        $cost->maxPrice = $this->data['maxPrice'] ?? 0;

        //检测exceed
        $this->verifyExceed($cost);

        switch ($this->costType)
        {
            case CostType::Hour:

                $cost->perHourPrice = $this->data['perHourPrice'] ?? 0;

                if (empty($cost->perHourPrice))
                    throw new \Exception('请设置每小时的价格');

                break;
            case CostType::HourStage:

                $cost->stageHours = $this->data['stageHours'] ?? 0;
                $cost->perStagePrice = $this->data['perStagePrice'] ?? 0;

                if (empty($cost->stageHours) || empty($cost->perStagePrice))
                    throw new \Exception('请设置包段小时数 和 价格');

                break;
            case CostType::BlockBooking:

                $blockBookingStages = $this->data['blockBookingStages'] ?? [];

                if (count($blockBookingStages) != 2)
                    throw new \Exception('包场场次必须为两场');

                foreach ($blockBookingStages as $bookingStage) {

                    $_bs = new BlockBookingStage();
                    $_bs->price = $bookingStage['price'];
                    $_bs->endHour = $bookingStage['endHour'];
                    $_bs->startHour = $bookingStage['startHour'];

                    $cost->blockBookingStages[] = $_bs;
                }

                if ($cost->exceedType == ExceedType::BlockBooking) {

                    $cost->exceedBlockBookingMinutes = $this->data['exceedBlockBookingMinutes'] ?? 0;
                    $cost->exceedPerHourPrice = $this->data['exceedPerHourPrice'] ?? 0;
                }

                break;
            case CostType::Minutes:
                $cost->perMinutesPrice = $this->data['perMinutesPrice'] ?? 0;

                if ($cost->perMinutesPrice <= 0)
                    throw new \Exception('请设置每分钟的价格');

                break;
            default:
                throw new \Exception('Failed cost type');
        }

        return $cost;
    }

    public function setType(string $type)
    {
        $this->costType = CostType::from($type);

        return $this;
    }

    private function verifyExceed(AbstractCost $cost)
    {
        if ($cost->exceedType == ExceedType::Interval) {

            if ($cost->exceedStageMinutes <= $cost->exceedFreeMinutes)
                throw new \Exception('超时后限免分钟数 不得大于 开始计费分钟数');
        }
    }

    /**
     * @return AbstractCost
     */
    private function getCostObj()
    {
        return new $this->costs[$this->costType->value];
    }

    private function bindCosts()
    {
        $this->costs = [
            CostType::Hour->value => HourCost::class,
            CostType::HourStage->value => HourStageCost::class,
            CostType::BlockBooking->value => BlockBookingCost::class,
            CostType::Minutes->value => MinutesCost::class,
        ];
    }
}
